﻿using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using Ycg.Utility;

/*
 * 关于RSA加密的一些知识
 * 
 * RSA加密只是意味着少量的数据，数据可以加密的数量取决于您正在使用的键的大小，
 * 例如1024比特的RSA密钥，和PKCS 1 v1.5 #填充，可以加密117个字节最多，有2048的RSA密钥，您可以加密245字节。
 * 
 * 这是有原因的，非对称加密在计算上是昂贵的。如果你想加密大量数据，你应该使用对称加密。
 * 但如果你想抵赖？你要做的就是使用。你创建一个对称密钥交换使用非对称加密，然后安全地交换对称密钥来加密你的数据量大。这就是SSL和WS安全在覆盖。
 */

namespace Ycg.Security
{
    public static class RSA
    {
        private static readonly string PublicKeyPath = Environment.CurrentDirectory + @"\publicKey.txt";
        private static readonly string PrivateKeyPath = Environment.CurrentDirectory + @"\privateKey.txt";

        private static string _publicKeyValue = string.Empty;
        private static string _privateKeyValue = string.Empty;

        public static string PublickKey
        {
            get { return _publicKeyValue; }
            set { _publicKeyValue = value; }
        }

        public static string PrivateKey
        {
            get { return _privateKeyValue; }
            set { _publicKeyValue = value; }
        }

        /// <summary>
        /// 创建RSA公钥和密钥
        /// </summary>
        /// <param name="publicKeyPath">公钥保存的路径(内容是Xml格式的)</param>
        /// <param name="privateKeyPath">密钥保存的路径(内容是Xml格式的)</param>
        /// <param name="keySize">钥匙长度，1024对应只能117个字节进行加密，2028对应245个字节</param>
        public static void CreateRSAKey(string publicKeyPath, string privateKeyPath, int keySize = 2048)
        {
            using (RSACryptoServiceProvider rsaCryptoService = new RSACryptoServiceProvider(keySize))
            {
                FileHelper.WriteContent(publicKeyPath, rsaCryptoService.ToXmlString(false)); //保存公钥
                FileHelper.WriteContent(privateKeyPath, rsaCryptoService.ToXmlString(true)); //保存公钥和私钥
            }
        }

        /// <summary>
        /// RSA加密
        /// 1.UTF8转换成字节
        /// 2.RSA通过公钥加密再进行Base64转码
        /// </summary>
        /// <param name="xmlPublicKey">公钥</param>
        /// <param name="content">加密前的文本</param>
        /// <returns>加密后的数据</returns>
        public static string Encrypt(string xmlPublicKey, string content)
        {
            using (RSACryptoServiceProvider rsaCryptoService = new RSACryptoServiceProvider())
            {
                rsaCryptoService.FromXmlString(xmlPublicKey);
                byte[] bytes = Encoding.UTF8.GetBytes(content);
                int bufferSize = (rsaCryptoService.KeySize / 8) - 11; //1024-117 : 2048-245

                if (bytes.Length > bufferSize)
                {
                    using (MemoryStream inMemoryStream = new MemoryStream(bytes))
                    {
                        using (MemoryStream outMemoryStream = new MemoryStream())
                        {
                            int readLine;
                            byte[] buffer = new byte[bufferSize];
                            while ((readLine = inMemoryStream.Read(buffer, 0, bufferSize)) > 0)
                            {
                                byte[] temp = new byte[readLine];
                                Array.Copy(buffer, 0, temp, 0, readLine);
                                byte[] encyrptBytes = rsaCryptoService.Encrypt(temp, false);
                                outMemoryStream.Write(encyrptBytes, 0, encyrptBytes.Length);
                            }
                            return Convert.ToBase64String(outMemoryStream.ToArray());
                        }
                    }
                }

                return Convert.ToBase64String(rsaCryptoService.Encrypt(bytes, false));
            }
        }

        /// <summary>
        /// RSA解密
        /// 1.通过Base64转换成字节
        /// 2.RSA通过私钥解密在进行UTF8编码
        /// </summary>
        /// <param name="xmlPrivateKey">私钥</param>
        /// <param name="content">解密前的文本</param>
        /// <returns>解密后的数据</returns>
        public static string Decrypt(string xmlPrivateKey, string content)
        {
            using (RSACryptoServiceProvider rsaCryptoService = new RSACryptoServiceProvider())
            {
                rsaCryptoService.FromXmlString(xmlPrivateKey);
                byte[] byteTemp = Convert.FromBase64String(content);

                int bufferSize = rsaCryptoService.KeySize / 8; //1024-128 : 2048:256

                if (byteTemp.Length > bufferSize)
                {
                    using (MemoryStream inMemoryStream = new MemoryStream(byteTemp))
                    {
                        using (MemoryStream outMemoryStream = new MemoryStream())
                        {
                            int readLine;
                            byte[] buffer = new byte[bufferSize];
                            while ((readLine = inMemoryStream.Read(buffer, 0, bufferSize)) > 0)
                            {
                                byte[] temp = new byte[readLine];
                                Array.Copy(buffer, 0, temp, 0, readLine);
                                byte[] decryptBytes = rsaCryptoService.Decrypt(temp, false);
                                outMemoryStream.Write(decryptBytes, 0, decryptBytes.Length);
                            }
                            return Encoding.UTF8.GetString(outMemoryStream.ToArray());
                        }
                    }
                }

                return Encoding.UTF8.GetString(rsaCryptoService.Decrypt(byteTemp, false));
            }
        }

        /// <summary>
        /// 用私钥对加密后的数据进行签名
        /// </summary>
        /// <param name="xmlPrivateKey">私钥</param>
        /// <param name="content">加密后的数据</param>
        /// <returns>签名后的数据</returns>
        public static string Signature(string xmlPrivateKey, string content)
        {
            return Signature(xmlPrivateKey, content, "SHA-512");
        }

        /// <summary>
        /// 用私钥对加密后的数据进行签名
        /// </summary>
        /// <param name="xmlPrivateKey">私钥</param>
        /// <param name="content">加密后的数据</param>
        /// <param name="signatureAlgorithm">签名算法</param>
        /// <returns>签名后的数据</returns>
        public static string Signature(string xmlPrivateKey, string content, string signatureAlgorithm)
        {
            byte[] bytes = Convert.FromBase64String(content);
            using (RSACryptoServiceProvider rsaCryptoService = new RSACryptoServiceProvider())
            {
                rsaCryptoService.FromXmlString(xmlPrivateKey);
                RSAPKCS1SignatureFormatter signatureFormatter = new RSAPKCS1SignatureFormatter(rsaCryptoService);
                signatureFormatter.SetHashAlgorithm(signatureAlgorithm);
                byte[] signBytes = signatureFormatter.CreateSignature(bytes);
                return Convert.ToBase64String(signBytes);
            }
        }

        //public static bool SignatureDeformatter(string xmlPublicKey, string content)
        //{
        //    byte[] bytes = Convert.FromBase64String(content);
        //    using (RSACryptoServiceProvider rsaCryptoService = new RSACryptoServiceProvider())
        //    {
        //        rsaCryptoService.FromXmlString(xmlPublicKey);
        //        RSAPKCS1SignatureDeformatter signatureDeformatter = new RSAPKCS1SignatureDeformatter();
        //        signatureDeformatter.SetHashAlgorithm("");

        //    }
        //}

        //public static string Encrypt(string encryptString)
        //{
        //    if (string.IsNullOrEmpty(_publicKeyValue))
        //        GeneratePublicAndPrivateKey();
        //    return Encrypt(_publicKeyValue, encryptString);
        //}

        //public static string Encrypt(string xmlPublicKey, string encryptString)
        //{
        //    using (RSACryptoServiceProvider rsaCryptoService = new RSACryptoServiceProvider())
        //    {
        //        rsaCryptoService.FromXmlString(xmlPublicKey);
        //        byte[] bytes = Encoding.UTF8.GetBytes(encryptString);
        //        byte[] encryptBytes = rsaCryptoService.Encrypt(bytes, false);
        //        return Convert.ToBase64String(encryptBytes);
        //    }
        //}

        //public static string Decrypt(string decryptString)
        //{
        //    if (string.IsNullOrEmpty(_publicKeyValue))
        //        GeneratePublicAndPrivateKey();
        //    return Decrypt(_privateKeyValue, decryptString);
        //}

        //public static string Decrypt(string xmlPrivateKey, string decryptString)
        //{
        //    using (RSACryptoServiceProvider rsaCryptoService = new RSACryptoServiceProvider())
        //    {
        //        rsaCryptoService.FromXmlString(xmlPrivateKey);
        //        byte[] bytes = Convert.FromBase64String(decryptString);
        //        byte[] decryptBytes = rsaCryptoService.Decrypt(bytes, false);
        //        return Encoding.UTF8.GetString(decryptBytes);
        //    }
        //}

        #region Write - Read (Public And Private Key)

        private static void GeneratePublicAndPrivateKey()
        {
            if (!System.IO.File.Exists(PublicKeyPath))
            {
                WritePublicAndPrivateKey();
            }
            if (string.IsNullOrEmpty(_publicKeyValue))
            {
                ReadPublicAndPrivateKey();
            }
        }

        private static void ReadPublicAndPrivateKey()
        {
            _publicKeyValue = System.IO.File.ReadAllText(PublicKeyPath);
            _privateKeyValue = System.IO.File.ReadAllText(PrivateKeyPath);
        }

        private static void WritePublicAndPrivateKey()
        {
            using (RSACryptoServiceProvider cryptoService = new RSACryptoServiceProvider())
            {
                string publicKey = cryptoService.ToXmlString(false);
                string privateKey = cryptoService.ToXmlString(true);

                System.IO.File.WriteAllText(PublicKeyPath, publicKey);
                System.IO.File.WriteAllText(privateKey, privateKey);
            }
        }

        #endregion
    }
}